#include "rwfile.h"

/**
 * 将读写作用于文件上
*/

ssize_t read_file(const char *filename, char *buf, size_t size, loff_t offset)
{
    struct file *fp;
    ssize_t bytes_read;

    fp = filp_open(filename, O_RDONLY, 0);
    if (IS_ERR(fp)) {
        pr_alert("Failed to open file %s\n", filename);
        return PTR_ERR(fp);
    }

    bytes_read = kernel_read(fp, buf, size, &offset);
    if (bytes_read < 0) {
        pr_alert("Failed to read file %s\n", filename);
        goto end;
    }

end:
    filp_close(fp, NULL);
    return bytes_read;
}

ssize_t write_file(const char *filename, const char *buf, size_t size,
               loff_t offset)
{
    struct file *fp;
    ssize_t bytes_written;

    fp = filp_open(filename, O_WRONLY | O_CREAT, 0666);
    if (IS_ERR(fp))
    {
        printk(KERN_ALERT "Failed to open file %s\n", filename);
        return PTR_ERR(fp);
    }

    // vfs_llseek(fp, offset, SEEK_SET);

    bytes_written = kernel_write(fp, buf, size, &offset);
    if (bytes_written < 0) {
        pr_alert("Failed to write file %s\n", filename);
        goto end;
    } else if (bytes_written != size) {
        pr_alert("write failed: bytes_written=%ld, size=%ld", bytes_written, size);
        goto end;
    }

    // printk(KERN_CONT "File content of %s: %s\n", filename, buf);

end:
    filp_close(fp, NULL);
    return bytes_written;
}

/**
 * BUGS: 注意这里使用了O_RDWR, 需要尽量避免使用fp->pos
*/
#define open_file_trunc(fp, filename) \
    fp = filp_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0666);\
    if (IS_ERR(fp)) {\
        printk(KERN_ALERT "Failed to open file %s\n", filename);\
        return PTR_ERR(fp);\
    }

struct file *fp_simple, *fp_dedup, *fp_meta;
uint32_t rSimple, wSimple, rDedup, wDedup, rMeta, wMeta;

int init_massiveRW(void) {
    open_file_trunc(fp_simple, SIMPLE_FILE);
    open_file_trunc(fp_dedup, DEDUP_FILE);
    open_file_trunc(fp_meta, META_FILE);
    rSimple = wSimple = rDedup = wDedup = rMeta = wMeta = 0;
    return 0;
}

void end_massiveRW(void) {
    filp_close(fp_simple, NULL);
    filp_close(fp_dedup, NULL);
    filp_close(fp_meta, NULL);
    return;
}

ssize_t read_file_fp(struct file *fp, char *buf, size_t size, loff_t offset)
{
    ssize_t bytes_read;
    bytes_read = kernel_read(fp, buf, size, &offset);
    if (bytes_read < 0) {
        pr_alert("fp: %s\n", fp->f_path.dentry->d_name.name);
        pr_alert("Failed to read file, errno: %d\n", (int)bytes_read);
    }
    return bytes_read;
}

ssize_t write_file_fp(struct file *fp, const char *buf, size_t size, loff_t offset)
{
    ssize_t bytes_written;
    bytes_written = kernel_write(fp, buf, size, &offset);
    if (bytes_written < 0) {
        pr_alert("Failed to write file, errno: %d\n", (int)bytes_written);
    } else if (bytes_written != size) {
        pr_alert("write failed: bytes_written=%ld, size=%ld", bytes_written, size);
    }
    return bytes_written;
}

// 将文件里面的数据按照BLKSZ进行分割换行
void split_data_file(char *src, char *dst) {
    char *buf = Kzalloc(BLKSZ + 1);
    int i, rd;
    for (i = 0; ; i++) {
        rd = read_file(src, buf, BLKSZ, i * BLKSZ);
        if (rd != BLKSZ) break;
        buf[BLKSZ] = '\n';
        write_file(dst, buf, BLKSZ+1, i*(BLKSZ+1));
    }
    return;
}

// 将文件里面的数据按照BLKSZ进行分割换行
void split_all_data_file(void) {
    split_data_file(SIMPLE_FILE, "temp_simple_split.txt");
    split_data_file(DEDUP_FILE, "temp_dedup_split.txt");
    split_data_file(META_FILE, "temp_meta_split.txt");
}

// 按行读取文件
// 每次都读maxsz个字节, 但是只返回一行, 适合每行大小都差不多的文件
int kernel_readline(char *buf, int maxsz, struct file *fp) {
    int i, rd;
    /**
     * BUGS: 注意这里使用了fp->f_pos, 可能会对其他的读写造成影响
    */
    rd = kernel_read(fp, buf, maxsz, &(fp->f_pos));
    if (rd < 1) {
        return 0;
    }

    for (i = 0; i < rd; i++) {
        if (buf[i] != '\n') continue;
        buf[i] = '\0';
        break;
    }
    
    // 如果没有换行, 这里选择了直接退出
    if (i == rd) {
        fp->f_pos -= rd;
        return 0;
    }

    i++;
    fp->f_pos += -rd + i;

    return i;
}


/**
 * 缓冲区按行读取工具函数
 * 每次从文件中读取maxsz个字节, 然后返回buf中的一行, 在保证超长行可以读取的同时, 也使用buffer保证较短行的读取效率
 * NOTE: 不要修改buf中的内容, 此函数会直接直接返回buf中对应位置的指针
 * NOTE: 如果有一行超过了maxsz, 那么迭代就会直接停止
 * BUGS: 如果最后一行没有换行符, 那么就不会被返回
 * BUGS: 使用了fp->f_pos
 * usage:
 *  fill_buffer(buf, BUFSZ, fp);
 *  while ((p = kernel_readline_buffered(buf, BUFSZ, fp)) != NULL) {
 *      pr_alert("%s", p);
 *  }
*/
static int buf_index = 0;
int fill_buffer(char *buf, int maxsz, struct file *fp) {
    loff_t pos = fp->f_pos;
    int rd = kernel_read(fp, buf, maxsz, &pos);
    buf_index = 0;
    return rd;
}

char * kernel_readline_buffered(char *buf, int maxsz, struct file *fp) {
    int i = buf_index;
    char *ret = buf + buf_index;

    while (i < maxsz && buf[i] != '\n') i++;

    if (i == maxsz) {
        // 如果没找到换行符, 再修改f_pos, 然后fill_buffer
        fp->f_pos += buf_index;
        if (fill_buffer(buf, maxsz, fp) < 1) {
            return NULL;
        }

        // 把上面的内容重来一遍
        i = 0;
        ret = buf;
        while (i < maxsz && buf[i] != '\n') i++;

        /**
         * 如果还是没找到换行符, 那就是真没有了
         * BUGS: 如果最后一行没有换行符, 那么就不会被返回
         * 可以尝试判断fill_buffer的返回值, 来判断是否已经读完
        */
        if (i == maxsz) {
            return NULL;
        }
    }

    buf[i] = '\0';
    buf_index = i + 1;
    return ret;
}
